import config from '@/config';
import { ApplicationModel } from '@/models/application';
import { PurePageModel } from '@/models/pure-page';
import { pathIsOk } from '@/utils/tools';
import fs from 'fs/promises';
import path from 'path';
import { Socket } from 'socket.io';
import { z } from 'zod';

import EmitTask from '../utils/emitTask';
import runCommand from '../utils/runCommand';

// 定义页面处理结果类型
type PageOperationResult = null | string;

// 定义页面数据类型
interface PageData {
  appId: string;
  name: string;
  readme: string;
}

const appInfoSchema = z.object({
  id: z.string({
    invalid_type_error: '应用ID必须是字符串',
    required_error: '应用ID不能为空',
  }),
});

/**
 * 获取应用中的页面信息并更新到数据库
 *
 * 此函数会扫描应用目录下的src/views文件夹，获取所有页面信息，
 * 并读取每个页面目录下的README.md文件内容，然后更新或创建对应的数据库记录。
 *
 * @param socket - Socket.io实例，用于实时通信
 * @param data - 包含应用ID的数据对象
 * @returns Promise<void>
 */
export default async function (socket: Socket, data: unknown): Promise<void> {
  const emit = new EmitTask(socket, 'app-info-update');
  emit.start();

  try {
    // 验证输入数据
    const validatedData = appInfoSchema.parse(data);
    const { id } = validatedData;

    // 获取应用信息
    const app = await ApplicationModel.findOne({ _id: id });
    if (!app) {
      return emit.errorEnd('应用不存在');
    }

    // 应用目录
    const repoDir = path.join(config.repoPath, id);
    // 检查应用目录是否存在
    if (!(await pathIsOk(repoDir))) {
      return emit.errorEnd(`应用目录 ${repoDir} 不存在`);
    }

    // 更新代码
    await runCommand(emit, 'git', ['pull'], {
      cwd: repoDir,
      shell: true,
    });

    const viewsDir = path.join(repoDir, 'src', 'packages');
    // 检查views目录是否存在
    if (!(await pathIsOk(viewsDir))) {
      return emit.errorEnd(`视图目录 ${viewsDir} 不存在`);
    }

    // 获取views目录下的所有页面文件夹
    const list = await fs.readdir(viewsDir);
    emit.info(`找到 ${list.length} 个页面`);

    // 并行处理所有页面信息以提高性能
    const operations: Promise<PageOperationResult>[] = list.map(pageName =>
      operationPage(emit, id, pageName, viewsDir),
    );

    // 等待所有页面处理完成
    const results = await Promise.all(operations);
    const successCount = results.filter(Boolean).length;

    emit.info(`成功处理 ${successCount}/${list.length} 个页面`);
    emit.successEnd();
  } catch (err) {
    if (err instanceof z.ZodError) {
      // 处理验证错误
      return emit.errorEnd(`数据验证失败: ${err.errors.map(e => e.message).join(', ')}`);
    }

    // 处理其他类型错误
    const errorMessage = err instanceof Error ? `${err.name}: ${err.message}` : String(err);

    return emit.errorEnd(`处理出错: ${errorMessage}`);
  }
}

/**
 * 处理单个页面信息
 * @param emit - EmitTask实例，用于发送任务状态
 * @param appId - 应用ID
 * @param pageName - 页面名称
 * @param viewsDir - 视图目录路径
 * @returns 成功返回页面名称，失败返回null
 */
async function operationPage(
  emit: EmitTask,
  appId: string,
  pageName: string,
  viewsDir: string,
): Promise<PageOperationResult> {
  try {
    const readmePath = path.join(viewsDir, pageName, 'README.md');
    const hasReadme = await pathIsOk(readmePath);

    // 检查README.md是否存在
    if (!hasReadme) {
      emit.info(`页面 ${pageName} 的README.md不存在，将使用空内容`);
    }

    // 读取README内容（如果存在）
    let readme = '';
    if (hasReadme) {
      try {
        readme = await fs.readFile(readmePath, 'utf-8');
        emit.info(`获取页面 ${pageName} 信息成功`);
      } catch (readErr) {
        emit.error(`读取页面 ${pageName} 的README.md失败: ${String(readErr)}`);
      }
    }

    if (readme) {
      // 替换图片链接 view.png
      // view.png 为 组件预览图像
      readme = replaceImageLinks(appId, pageName, readme);
    }

    // 查找是否已存在该页面记录
    const existingPage = await PurePageModel.findOne({ appId, name: pageName });

    // 根据是否存在记录执行不同操作
    if (existingPage) {
      // 更新现有记录
      emit.info(`页面 ${pageName} 已存在，更新信息`);
      await PurePageModel.updateOne({ _id: existingPage._id }, { readme });
    } else {
      // 创建新记录
      emit.info(`页面 ${pageName} 信息获取完成，写入数据库`);
      const pageData: PageData = {
        appId,
        name: pageName,
        readme,
      };
      await PurePageModel.create(pageData);
    }

    return pageName;
  } catch (pageErr) {
    // 单个页面处理错误不应该中断整个流程
    const errorMessage =
      pageErr instanceof Error ? `${pageErr.name}: ${pageErr.message}` : String(pageErr);

    emit.error(`处理页面 ${pageName} 时出错: ${errorMessage}`);
    return null;
  }
}

/**
 * 替换README.md中图片的链接地址
 * `http://${config.ip}:${config.port}/repoStatic/${appId}/src/packages/${pageName}/view.png`
 * @param appId - 应用ID
 * @param pageName - 页面名称
 * @param readme - README.md内容
 */
export function replaceImageLinks(appId: string, pageName: string, readme: string): string {
  // 替换图片链接
  // ![图片描述]('./view.png')
  // => ![图片描述](`http://${config.ip}:${config.port}/repoStatic/${appId}/src/packages/${pageName}/view.png`)
  const pattern = /!\[([^\]]*)]\(\.\/view\.png\)/g;

  return readme.replace(pattern, (match, p1) => {
    return `![${p1}](http://${config.server.ip}:${config.server.port}/repoStatic/${appId}/src/packages/${pageName}/view.png)`;
  });
}
